// Burner data file module (for ROM managers)
// written    2001 LoqiqX
// updated 11/2003 by LvR    -- essentially a rewrite
// updated    2008 by regret -- support clrmamepro xml dat

#include "burner.h"
#include "../lib/ticpp/ticpp.h"

// write clrmamepro xml, added by regret
int createDatFile(const wchar_t* filename, FILE* file)
{
	try {
		char str[MAX_PATH];
		sprintf(str, "%ls v%.20ls", APP_TITLE, appBurnVer);

		// root
		ticpp::Document doc;
		ticpp::Declaration decl("1.0", "UTF-8", "");
		doc.LinkEndChild(&decl);
		ticpp::Element root("datafile");
		root.SetAttribute("build", str);
		doc.LinkEndChild(&root);

		// header
		ticpp::Element header("header");
		ticpp::Element name("name", WtoA(APP_TITLE));
		ticpp::Element desc("description", str);
		ticpp::Element version("version", WtoA(appBurnVer));
		header.LinkEndChild(&name);
		header.LinkEndChild(&desc);
		header.LinkEndChild(&version);
		root.LinkEndChild(&header);

		// game
		int ret = 0;
		unsigned parentSelect, boardROMSelect;

		char sgName[MAX_PATH];
		char spName[MAX_PATH];
		char sbName[MAX_PATH];
		unsigned i = 0;
		int pass = 0;

		unsigned drvCount = BurnGetDrvCount();

		// Go over each of the games
		for (unsigned gameSelect = 0; gameSelect < drvCount; gameSelect++) {
			if (BurnDrvGetFlags(gameSelect) & BDF_BOARDROM) {
				continue;
			}

			strcpy(sgName, BurnDrvGetTextA(DRV_NAME, gameSelect));
			strcpy(spName, ""); 	// make sure this string is empty before we start
			strcpy(sbName, ""); 	// make sure this string is empty before we start

			// Check to see if the game has a parent
			parentSelect = gameSelect;
			if (BurnDrvGetTextA(DRV_PARENT, parentSelect)) {
				strcpy(spName, BurnDrvGetTextA(DRV_PARENT, parentSelect));
				parentSelect = BurnDrvGetIndexByNameA(spName);
				if (parentSelect >= drvCount) {
					parentSelect = ~0U;
				}
			}

			// Check to see if the game has a BoardROM
			boardROMSelect = gameSelect;
			if (BurnDrvGetTextA(DRV_BOARDROM, boardROMSelect)) {
				strcpy(sbName, BurnDrvGetTextA(DRV_BOARDROM, boardROMSelect));
				boardROMSelect = BurnDrvGetIndexByNameA(sbName);
				if (boardROMSelect >= drvCount) {
					boardROMSelect = ~0U;
				}
			}

			// Write the header
			ticpp::Element game("game");
			game.SetAttribute("name", sgName);

			if (parentSelect != gameSelect && parentSelect != ~0U
				&& (BurnDrvGetFlags(gameSelect) & BDF_CLONE)) {
				game.SetAttribute("cloneof", spName);
				game.SetAttribute("romof", spName);
			} else {
				// Add "romof" (but not 'cloneof') line for games that have boardROMs
				if (boardROMSelect != gameSelect && boardROMSelect != ~0U) {
					game.SetAttribute("romof", sbName);
				}
			}

			ticpp::Element gamedesc("description", decorateGameName(gameSelect));
			ticpp::Element gameyear("year", BurnDrvGetTextA(DRV_DATE, gameSelect));
			ticpp::Element gamemanuf("manufacturer", BurnDrvGetTextA(DRV_MANUFACTURER, gameSelect));
			game.LinkEndChild(&gamedesc);
			game.LinkEndChild(&gameyear);
			game.LinkEndChild(&gamemanuf);

			// Write the individual ROM info
			for (pass = 0; pass < 2; pass++) {
				// Skip pass 0 if possible
				if (pass == 0 && (boardROMSelect == gameSelect || boardROMSelect == ~0U))
					continue;

				// Go over each of the files needed for this game (upto 0x0100)
				for (i = 0, ret = 0; ret == 0 && i < 0x100; i++) {
					int retTmp = 0;
					struct BurnRomInfo ri;
					unsigned len, crc;
					char *possibleName = NULL;
					int j, merged = 0;

					memset(&ri, 0, sizeof(ri));

					// Get info on this file
					ret = BurnDrvGetRomInfo(&ri, i, gameSelect);
					ret += BurnDrvGetRomName(&possibleName, i, 0, gameSelect);

					if (ri.nLen == 0) continue;

					if (ret == 0) {
						struct BurnRomInfo riTmp;
						char *szPossibleNameTmp;
						len = ri.nLen; crc = ri.nCrc;

						// Check for files from boardROMs
						if (boardROMSelect != gameSelect && boardROMSelect != ~0U) {
							retTmp = 0;

							// Go over each of the files needed for this game (upto 0x0100)
							for (j = 0; retTmp == 0 && j < 0x100; j++) {
								memset(&riTmp, 0, sizeof(riTmp));

								retTmp += BurnDrvGetRomInfo(&riTmp, j, boardROMSelect);
								retTmp += BurnDrvGetRomName(&szPossibleNameTmp, j, 0, boardROMSelect);

								if (retTmp == 0) {
									if (riTmp.nLen && riTmp.nCrc == crc && !strcmp(possibleName, szPossibleNameTmp)) {
										// This file is from a boardROM
										merged |= 2;
										retTmp++;
									}
								}
							}
						}

						if (!merged && parentSelect != gameSelect && parentSelect != ~0U) {
							retTmp = 0;

							// Go over each of the files needed for this game (upto 0x0100)
							for (j = 0; retTmp == 0 && j < 0x100; j++) {
								memset(&riTmp, 0, sizeof(riTmp));

								retTmp += BurnDrvGetRomInfo(&riTmp, j, parentSelect);
								retTmp += BurnDrvGetRomName(&szPossibleNameTmp, j, 0, parentSelect);

								if (retTmp==0) {
									if (riTmp.nLen && riTmp.nCrc == crc && !strcmp(possibleName, szPossibleNameTmp)) {
										// This file is from a parent set
										merged |= 1;
										retTmp++;
									}
								}
							}
						}
					}

					ticpp::Element rom("rom");
					bool addNode = false;

					// Selectable BIOS meta info
					if (pass == 0 && (merged & 2) && (ri.nType & BRF_SELECT)) {
//						fprintf(file, "\tbiosset (name %d description \"%s\" %s)\n",
//							i - 128, possibleName, ri.nType & BRF_OPT ? "" : "default yes ");
					}

					// File info
					if (pass == 1 && !merged) {
						rom.SetAttribute("name", possibleName);
						rom.SetAttribute("size", ri.nLen);

						if (ri.nCrc == 0) {
							rom.SetAttribute("status", "nodump");
						} else {
							sprintf(str, "%08x", ri.nCrc);
							rom.SetAttribute("crc", str);
						}

						if (ri.nCrc != 0 && (ri.nType & BRF_NODUMP)) {
							rom.SetAttribute("status", "baddump");
						}
						addNode = true;
					}
					if (pass == 1 && merged) {
						rom.SetAttribute("name", possibleName);
						rom.SetAttribute("merge", possibleName);
						rom.SetAttribute("size", ri.nLen);

						// Selectable BIOS file info
						if (merged & 2 && ri.nType & BRF_SELECT) {
							sprintf(str, "%08x", ri.nCrc);
							rom.SetAttribute("crc", str);
						}
						// Files from parent/boardROMs
						else {
							if (ri.nCrc == 0) {
								rom.SetAttribute("status", "nodump");
							} else {
								sprintf(str, "%08x", ri.nCrc);
								rom.SetAttribute("crc", str);
							}

							if (ri.nCrc != 0 && (ri.nType & BRF_NODUMP)) {
								rom.SetAttribute("status", "baddump");
							}
						}
						addNode = true;
					}

					if (addNode) {
						game.LinkEndChild(&rom);
					}
				}
			}

			root.LinkEndChild(&game);
		}

		// Do another pass over each of the games to find boardROMs
		for (unsigned select = 0; select < drvCount; select++) {
			if (!(BurnDrvGetFlags(select) & BDF_BOARDROM)) {
				continue;
			}

			ticpp::Element game("game");
			game.SetAttribute("isbios", "yes");
			game.SetAttribute("name", BurnDrvGetTextA(DRV_NAME, select));
			ticpp::Element gamedesc("description", decorateGameName(select));
			ticpp::Element gameyear("year", BurnDrvGetTextA(DRV_DATE, select));
			ticpp::Element gamemanuf("manufacturer", BurnDrvGetTextA(DRV_COMMENT, select));
			game.LinkEndChild(&gamedesc);
			game.LinkEndChild(&gameyear);
			game.LinkEndChild(&gamemanuf);

			for (int pass = 0; pass < 2; pass++) {
				// Go over each of the individual files (upto 0x0100)
				for (int i = 0; i < 0x100; i++) {
					struct BurnRomInfo ri;
					char *possibleName = NULL;

					memset(&ri, 0, sizeof(ri));

					ret = BurnDrvGetRomInfo(&ri, i, select);
					ret += BurnDrvGetRomName(&possibleName, i, 0, select);

					if (ri.nLen == 0) continue;

					if (ret == 0) {
						if (pass == 0) {
							if (ri.nType & BRF_SELECT) {
//								fprintf(file, "\tbiosset ( name %d description \"%s\" %s)\n", i, possibleName, ri.nType & 0x80 ? "" : "default yes ");
							}
						} else {
							sprintf(str, "%08x", ri.nCrc);

							ticpp::Element rom("rom");
							rom.SetAttribute("name", possibleName);
							rom.SetAttribute("size", ri.nLen);
							rom.SetAttribute("crc", str);
							game.LinkEndChild(&rom);
						}
					}
				}
			}

			root.LinkEndChild(&game);
		}

		// write xml
		if (file) {
			doc.SaveFile(file, TIXML_ENCODING_UTF8);
		} else {
			doc.SaveFile(WtoA(filename), TIXML_ENCODING_UTF8);
		}
	}
	catch (ticpp::Exception& ex) {
		return 1;
	}

	return 0;
}
